﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Autofac;
using Autofac.Features.Metadata;
using Microsoft.Practices.ServiceLocation;
using System.Reflection;

namespace BSF.Framework
{
    /// <summary>
    /// Autofac implementation of the Microsoft CommonServiceLocator.
    /// </summary>
    public class AutofacServiceLocator : ServiceLocatorImplBase
    {
        /// <summary>
        /// The <see cref="Autofac.IComponentContext"/> from which services
        /// should be located.
        /// </summary>
        private readonly IComponentContext _container;

        /// <summary>
        /// Initializes a new instance of the <see cref="AutofacServiceLocator" /> class.
        /// </summary>
        /// <param name="container">
        /// The <see cref="Autofac.IComponentContext"/> from which services
        /// should be located.
        /// </param>
        /// <exception cref="System.ArgumentNullException">
        /// Thrown if <paramref name="container" /> is <see langword="null" />.
        /// </exception>
        public AutofacServiceLocator(IComponentContext container)
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }
            _container = container;
        }

        public override TService GetInstance<TService>(string key)
        {
            if (key == null)
            {
                var item = _container.Resolve<IEnumerable<Meta<Lazy<TService>>>>()
                    .OrderByDescending(m => (m.Metadata.Keys.Count > 0 && m.Metadata.Keys.Contains("Priority")) ? (int)m.Metadata["Priority"] : 0)
                        .FirstOrDefault();
                if (item != null)
                    return item.Value.Value;
            }
            else
            {
                return _container.ResolveNamed<TService>(key);
            }
            return default(TService);
            //return key != null ? _container.ResolveNamed<TService>(key) : _container.Resolve<TService>();
        }

        public override TService GetInstance<TService>()
        {
            return GetInstance<TService>(null);
        }

        /// <summary>
        /// Resolves the requested service instance.
        /// </summary>
        /// <param name="serviceType">Type of instance requested.</param>
        /// <param name="key">Name of registered service you want. May be <see langword="null" />.</param>
        /// <returns>The requested service instance.</returns>
        /// <exception cref="System.ArgumentNullException">
        /// Thrown if <paramref name="serviceType" /> is <see langword="null" />.
        /// </exception>
        protected override object DoGetInstance(Type serviceType, string key)
        {
            if (serviceType == null)
            {
                throw new ArgumentNullException("serviceType");
            }

            if (key == null)
            {
                var type =
                    typeof(IEnumerable<>).MakeGenericType(
                        typeof(Meta<>).MakeGenericType(typeof(Lazy<>).MakeGenericType(serviceType)));

                var instance = _container.Resolve(type);
                var list = ((IEnumerable)instance).Cast<dynamic>();
                var result =
                    list.OrderByDescending(m => (m.Metadata.Keys.Count > 0 && m.Metadata.Keys.Contains("Priority")) ? (int)m.Metadata["Priority"] : 0)
                        .FirstOrDefault();
                if (result != null)
                {
                    return result.Value.Value;
                }
                return null;
            }
            else
            {
                return _container.ResolveNamed(key, serviceType);
            }

            //return key != null ? _container.ResolveNamed(key, serviceType) : _container.Resolve(serviceType);
        }

        /// <summary>
        /// Resolves all requested service instances.
        /// </summary>
        /// <param name="serviceType">Type of instance requested.</param>
        /// <returns>Sequence of service instance objects.</returns>
        /// <exception cref="System.ArgumentNullException">
        /// Thrown if <paramref name="serviceType" /> is <see langword="null" />.
        /// </exception>
        protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
        {
            if (serviceType == null)
            {
                throw new ArgumentNullException("serviceType");
            }
            var enumerableType = typeof(IEnumerable<>).MakeGenericType(serviceType);

            object instance = _container.Resolve(enumerableType);
            return ((IEnumerable)instance).Cast<object>();
        }
    }
    
}
